package com.fowo.api.sys.service.impl;

import cn.dustlight.captcha.core.Code;
import cn.dustlight.captcha.generator.CodeGenerator;
import cn.dustlight.captcha.sender.SimpleImageCodeSender;
import cn.dustlight.captcha.verifier.CodeVerifier;
import com.fowo.api.sys.service.SysRandomImageService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.util.Base64Utils;
import org.springframework.util.StringUtils;

import javax.annotation.Resource;
import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.Collections;
import java.util.concurrent.TimeUnit;

@Service
@Slf4j
public class SysRandomImageServiceImpl implements SysRandomImageService {
    /**
     * 验证码 Redis 缓存键名开头
     */
    public static final String REDIS_KEY_PREFIX = "SYS_RANDOM_IMAGE_CODE.";
    /**
     * 验证码有效时长（秒）
     */
    public static final long CODE_VALID_TIME_SECONDS = 300;
    /**
     * 生成的验证码长度
     */
    public static final int CODE_LENGTH = 4;

    @Resource
    private CodeGenerator<String> codeGenerator;
    @Resource
    private RedisTemplate<String, String> redisTemplate;
    @Resource
    private SimpleImageCodeSender<String> imageCodeSender;

    @Override
    public String getNewRandomImageBase64(String key) {
        final Code<String> code = codeGenerator.generate(key, Collections.singletonMap("CODE_LENGTH", CODE_LENGTH));
        saveCode(key, code);
        BufferedImage image = imageCodeSender.getImageHandler().getImage(code.getValue(), 105, 35, Collections.emptyMap());
        return toBase64(image);
    }

    private String toBase64(BufferedImage image) {
        ByteArrayOutputStream os = new ByteArrayOutputStream();
        try {
            ImageIO.write(image, "gif", os);
        } catch (IOException e) {
            e.printStackTrace();
        }
        byte[] bytes = os.toByteArray();
        String base64 = Base64Utils.encodeToString(bytes);
        return "data:image/gif;base64," + base64;
    }

    @Override
    public boolean valid(String key, String code) {
        String rawCode = redisTemplate.opsForValue().get(getRedisKey(key));
        // 验证码不管是否正确，只使用一次
        redisTemplate.delete(getRedisKey(key));
        if (StringUtils.hasText(code) && StringUtils.hasText(rawCode)) {
            return rawCode.equalsIgnoreCase(code.trim());
        }
        return false;
    }

    private void saveCode(String key, Code<String> code) {
        String codeValue = code.getValue();
        String redisKey = getRedisKey(key);
        redisTemplate.opsForValue().set(redisKey, codeValue, CODE_VALID_TIME_SECONDS, TimeUnit.SECONDS);
    }

    private String getRedisKey(String key) {
        return String.format("%s%s", REDIS_KEY_PREFIX, key);
    }
}
